package com.rich.dmr.server;

import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import android.content.Context;
import android.os.Handler;
import android.util.Log;

import com.rich.dmr.model.Frequency;
import com.rich.dmr.utils.Constant;
import com.rich.dmr.utils.DataBaseUtils;
import com.rich.dmr.utils.GetPropUtils;

public class ClientHandler {
    private final String TAG = "rich---ClientHandler";
    private final Socket socket;
    private final ClientReadHandler readHandler;
    private final ClientWriteHandler writeHandler;
    private final ClientHandlerCallback clientHandlerCallback;
    private final String clientInfo;
    private Context mContext;
    private Handler mHandler;

    ClientHandler(Socket socket,
                  ClientHandlerCallback clientHandlerCallback, Context context, Handler handler)
            throws IOException {
        mContext = context;
        this.socket = socket;
        this.readHandler = new ClientReadHandler(socket.getInputStream());
        this.writeHandler = new ClientWriteHandler(socket.getOutputStream());
        this.clientHandlerCallback = clientHandlerCallback;
        this.clientInfo = "A[" + socket.getInetAddress().getHostAddress()
                + "] P[" + socket.getPort() + "]";
        Log.d(TAG, "new client：" + clientInfo);
        mHandler = handler;
    }

    String getClientInfo() {
        return clientInfo;
    }

    void exit() {
        readHandler.exit();
        writeHandler.exit();
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.d(TAG, "client already exit!" + socket.getInetAddress() + " P:" + socket.getPort());
    }
    void send(String str) {
        writeHandler.send(str);
    }
    void readToPrint() {
        readHandler.start();
    }
    private void exitBySelf() {
        exit();
        clientHandlerCallback.onSelfClosed(this);
    }

    public interface ClientHandlerCallback {
        void onSelfClosed(ClientHandler handler);
        void onNewMessageArrived(ClientHandler handler, String msg);
    }

    class ClientReadHandler extends Thread {
        private boolean done = false;
        private final InputStream inputStream;

        ClientReadHandler(InputStream inputStream) {
            this.inputStream = inputStream;
        }

        @Override
        public void run() {
            super.run();
            try {
                DataInputStream dis = new DataInputStream(inputStream);
                do {
                    byte[] bytes = new byte[256];
                    int len;
                    StringBuilder sb = new StringBuilder();
                    try {
                        if ((len = dis.read(bytes)) != -1) {
                            sb.append(new String(bytes, 0, len, "UTF-8"));
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    if (sb.toString().equals("") || sb.toString().length() == 0) {
                        Log.d(TAG, "client can not read data!");
                        mHandler.sendEmptyMessage(5);
                        ClientHandler.this.exitBySelf();
                        break;
                    }
                    messageSwitch(sb.toString());
                } while (!done);
            } catch (Exception e) {
                if (!done) {
                    Log.d(TAG, "connect error!");
                    ClientHandler.this.exitBySelf();
                }
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        public void messageSwitch(String msg) {
            if ("1000".equals(msg)) {
                mHandler.removeMessages(0);
                mHandler.sendEmptyMessageDelayed(0, 4000);
                writeHandler.send("102");
            } else if(msg.contains("pc Connected")){
                getVersionAndModel();
            }else if (msg.contains(Constant.WRITE_FRE)) {
                String[] msgs = msg.split("/");
                String channelString = msgs[1];
                String txFreqString = msgs[2];
                String rxFreqString = msgs[3];
                if (channelString.length() > 0 && txFreqString.length() > 0 && rxFreqString.length() > 0) {
                    int channel = Integer.parseInt(channelString);
                    int txFrequency = (int) mul(Double.parseDouble(txFreqString), 1000000);
                    int rxFrequency = (int) mul(Double.parseDouble(rxFreqString), 1000000);

                    Log.d(TAG, "channel=" + channel + "txFrequency=" + txFrequency + "rxFrequency=" + rxFrequency);

                    if (txFrequency >= 400000000 && txFrequency <= 480000000 && rxFrequency >= 400000000 && rxFrequency <= 480000000) {
                        if (DataBaseUtils.getInstance(mContext).findFrequencyByFre(txFrequency, rxFrequency) == null) {
                            if (DataBaseUtils.getInstance(mContext).insertChannel(channel, txFrequency, rxFrequency)) {
                                writeHandler.send("channel:" + channel + "  success");
                                mHandler.sendEmptyMessageDelayed(Constant.MSG_CHANNEL_OBJ, 500);
                                Log.d(TAG, "data insert success..");
                            } else {
                                writeHandler.send("channel:" + channel + " fail");
                                Log.d(TAG, "data insert fail..");
                            }
                        } else {
                            writeHandler.send("channel:" + channel + " exist");
                            Log.d(TAG, "data exist..");
                        }
                    } else {
                        writeHandler.send("channel:" + channel + " out of range.");
                        Log.d(TAG, "data out of range..");
                    }
                } else {
                    writeHandler.send("channel:" + channelString + " no data.");
                    Log.d(TAG, "no data..");
                }
            } else if (msg.contains("color") && msg.contains("group")) {
                String[] msgs = msg.split("&");
                String color = msgs[0].split(":")[1];
                mHandler.obtainMessage(Constant.MSG_COLOR_OBJ, 0, Integer.parseInt(color)).sendToTarget();
                Log.d(TAG, "color=" + color);
                if (msgs[1].length() > 6) {
                    String group = msgs[1].split(":")[1];
                    if (group.length() <= 8) {
                        mHandler.obtainMessage(Constant.MSG_GROUP_OBJ, 0, Integer.parseInt(group)).sendToTarget();
                    }
                    Log.d(TAG, "group=" + group);
                }
                writeHandler.send("color:success");
            }else if (msg.equals("10000")) {
                Log.d(TAG, "start read");
                final List<Frequency> mList = DataBaseUtils.getInstance(mContext).findFrequencyList();
                if (mList == null) {
                    writeHandler.send("Frequency read:null");
                } else {
                    writeHandler.send("readStart");
                    for (Frequency fre : mList) {
                        try {
                            sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        writeHandler.send(fre.toString());
                    }
                    try {
                        sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    writeHandler.send("readEnd");
                }
            }
        }

        void getVersionAndModel(){
            writeHandler.send("DP:" + GetPropUtils.getProp("ro.product.model"));
            try {
                sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            writeHandler.send("DV:" + GetPropUtils.getProp("ro.custom.version"));
        }

        double mul(double v1, double v2) {
            BigDecimal b1 = new BigDecimal(Double.toString(v1));
            BigDecimal b2 = new BigDecimal(Double.toString(v2));
            return b1.multiply(b2).doubleValue();
        }

        void exit() {
            done = true;
            try {
                inputStream.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    static class ClientWriteHandler {
        private boolean done = false;
        private final PrintStream printStream;
        private final ExecutorService executorService;

        ClientWriteHandler(OutputStream outputStream) {
            this.printStream = new PrintStream(outputStream);
            this.executorService = Executors.newSingleThreadExecutor();
        }
        void exit() {
            done = true;
            printStream.close();
            executorService.shutdownNow();
        }
        void send(String str) {
            executorService.execute(new WriteRunnable(str));
        }
        class WriteRunnable implements Runnable {
            private final String msg;
            WriteRunnable(String msg) {
                this.msg = msg;
            }
            @Override
            public void run() {
                if (ClientWriteHandler.this.done) {
                    return;
                }
                try {
                    ClientWriteHandler.this.printStream.println(msg);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
